<?php

namespace App\Trip\Repository;

use Exception;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection;
use function app;

class BaseRepository implements BaseRepositoryInterface
{
    protected Model $model;

    public function __construct(Model $model)
    {
        $this->model = $model;
    }

    /**
     * @param array $attributes
     *
     * @return mixed
     */
    public function create(array $attributes): mixed
    {
        return $this->model->create($attributes);
    }

    /**
     * @param array      $attributes
     * @param Model|null $model
     *
     * @return bool
     */
    public function update(array $attributes, Model $model = null): bool
    {
        if (!is_null($model)) {
            return $model->update($attributes);
        }
        return $this->model->update($attributes);
    }

    /**
     * @param array  $columns
     * @param string $orderBy
     * @param string $sortBy
     *
     * @return mixed
     */
    public function all(array $columns = ['*'], string $orderBy = 'id', string $sortBy = 'asc'): mixed
    {
        return $this->model->orderBy($orderBy, $sortBy)->get($columns);
    }

    /**
     * @param string $id
     *
     * @return mixed
     */
    public function find($id): mixed
    {
        return $this->model->find($id);
    }

    /**
     * @param  $id
     *
     * @return mixed
     * @throws ModelNotFoundException
     */
    public function findOneOrFail($id): mixed
    {
        return $this->model->findOrFail($id);
    }

    /**
     * @param array         $data
     * @param callable|null $callback
     * @return Collection
     */
    public function findBy(array $data, callable $callback = null): mixed
    {
        return $this->model->where($data, $callback)->get();
    }

    protected function where($where, callable $callback = null)
    {
        $query = $this->model->where($where);
        if ($callback) {
            $query = $callback($query);
        }
        return $query;
    }

    /**
     * @param array $data
     *
     * @return mixed
     */
    public function findOneBy(array $data): mixed
    {
        return $this->model->where($data)->first();
    }

    /**
     * @param array $data
     *
     * @return mixed
     * @throws ModelNotFoundException
     */
    public function findOneByOrFail(array $data): mixed
    {
        return $this->model->where($data)->firstOrFail();
    }

    /**
     * @return bool
     * @throws Exception
     */
    public function delete(): bool
    {
        if (!$this->model->getKey()) {
            throw new Exception('not support');
        }
        return $this->model->delete();
    }

    /**
     * Paginate arrays
     *
     * @param array $data
     * @param int   $perPage
     *
     * @return LengthAwarePaginator
     */
    public function paginateArrayResults(array $data, int $perPage = 15): LengthAwarePaginator
    {
        $page   = app('request')->input('page', 1);
        $offset = ($page * $perPage) - $perPage;

        return new LengthAwarePaginator(
            array_values(array_slice($data, $offset, $perPage, true)),
            count($data),
            $perPage,
            $page,
            [
                'path'  => app('request')->url(),
                'query' => app('request')->query(),
            ]
        );
    }
}